#include "DocumentsProcessor.h"
#include "Registers.h"
#include "bstreewordidregisterclasses.h"
#include "../indice/bstree/src/bstree.h"
#include "../indice/bstree/src/bsIterator.h"
#include "ExternSort.h"
#include "IDFile.h"

#include <string.h>
/********************************************************************************************/
DocumentsProcessor::DocumentsProcessor(interprete* interpreter)
{
	//sourceDirectory			= source;
	//destinationDirectory	= dest;
	//interpreter = new implementacionInterprete();
	this->interpreter = interpreter;
	
	auxFile = NULL;
	docFile = NULL;
	contadorReg = 0;
}
/********************************************************************************************/
DocumentsProcessor::~DocumentsProcessor()
{
	//delete interpreter;
}
/********************************************************************************************/
void DocumentsProcessor::loadListDocuments(void)
{
	
}
/********************************************************************************************/
DocumentFile* DocumentsProcessor::getNextDocument(void)
{
	DocumentFile* g;
	int id;
	string nameFile;

	id = interpreter->obtenerProximoIDAProcesar();// me devuelve 1, 2, 3, ....... txt
		
	if(id!=-1)
	{
		nameFile = interpreter->strObtenerDocumentoHash(id);
		if(!nameFile.empty())
		{
			g = new DocumentFile( nameFile, id );
			if (g->openFile() && !g->empty())
				return g;
			else
				return NULL;
		}
		else 
			return NULL;
	}
	return NULL;
}
using namespace invertedindex;

/********************************************************************************************/
bool DocumentsProcessor::processDocuments(QProgressBar *progress, int cantidadDocumentos)
{
	
	InvertedIndex indice; // indice de terminos i Id de los mismos
	int ID, IDAux;
	Data word_offset; // contiene termino y offset contenidos en el documento
	tRegDictionary regDic; // contiene termino i Id del termino
	AuxiliaryRegister regAux; // contiene el Id de termino, Nº doc, y offset en el mismo
	string strID();
	
	IDFile *ptrIDFile = new IDFile("Misc/ID.txt");
	
	if(!ptrIDFile->openFile())
	{
		ptrIDFile->createFile();
		ptrIDFile->openFile();
		ID = 0;
	}
	else
	{
		ID = ptrIDFile->readLastID();
	}
	IDAux = ID;
	InvertedIndexData* data;
	
	
	/***************************************  INTERPRETE DE DOCS *********************************************/
	string nameFile;
		//me fijo si hay nuevos archivos que interpretar

//	if(interpreter->verificarArchivosNuevos())
//	{ //si hay archivos creo los temporales sino vuelvo y no hago nada

		progress->setValue(0);
		
		auxFile = new AuxiliaryFile("auxiliar.txt");
		auxFile->createFile();
		auxFile->openFile();
		
		/*dicFile = new DictionaryFile("diccionario.txt");
		dicFile->createFile();
		dicFile->openFile();*/
//	}
//	else
//	{
//		cout<<"NO EXISTEN DOCUMENTOS A PROCESAR"<<endl;
//		return false;
//	}
	//saliendo de aca al menos existe uno
	
	docFile = getNextDocument(); // obtengo el siguiente documento txt
	
	if ( docFile == NULL ) //hubo algun error por lo que no me trajo el documentos
		return false;
	unsigned int kss=0;
	//Creo arbol que contendra los id-palabra ya ingresados
	KeyValFactoryId kvfId;
	KeyKeyValFactoryId kkvfId;
	bstree::BSTree bsharpTree(512,kvfId ,kkvfId ,"./data/bstreewordid.dat");
	int auxprog = 0;
	while( docFile != NULL )
	{
		regAux.reset();
		regAux.setDocument( docFile->getId() ); // obtengo el Nº de documento a cargar en el archivo auxiliar
		int auxprog2 = 0;
		long cantPalabras = docFile->size()/9;
		double tmp2;
		while( docFile->nextWord(word_offset) && (!interpreter->esStopWord(word_offset.word)) ) // mientras haya terminos en el documento
		{
			data = new InvertedIndexData();
			memset(&regDic, 0, sizeof(regDic));
			//cout<<"Palabra: "<<word_offset.word.c_str()<<endl;
			//OJO: SI LA PALABRA ES MUY LARGA PINCHA, TIENE QEU VENIR UN CIERTO LIMITE DE CARACTERES
			word_offset.word = interpreter->aplicarStemming(word_offset.word);
			strcpy( regDic._word, word_offset.word.c_str() ); //regDic sirve para buscar en el indice
			
			data->setWordParameters(word_offset.word.size(),word_offset.word.c_str(),0);
			if(!indice.exist( *data )) // si ya existia el termino traigo el Id, sino lo agrego
			{
				data->setWordId(++ID);
				indice.insert(*data);
				/*regDic._Id = ID;
				dicFile->addRegister( regDic );*/
				
				regAux.setId( data->getWordId() ); // obtengo el Id del termino a cargar en el archivo auxiliar
				regAux.setOffset( word_offset.offset ); // obtengo el offset del termino en el documento a cargar en el archivo auxiliar
				// hasta aca tengo toda la data para cargar en el auxiliar
				auxFile->addRegister( regAux.getData() );
				
				//data->toOstream(std::cout);
				KeyValId wid(word_offset.word.size(),word_offset.word.c_str(), data->getWordId());
				bsharpTree.insert(wid);
				
				/*kss++;
				if(kss>=334){
					//kss=320;
					std::cout<<bsharpTree;
					printf("Leego a 100\n");
					
				}*/
				
			}
			else
			{
				if(IDAux>=data->getWordId())
				{
					/*indice.search(*data);
					unsigned int docCount=data->getDocumentsCount();
					unsigned int js = 0;
					for(js ; js<docCount;js++)
					{
						if(data->getIdOfDocument(js).second== docFile->getId())
							break;
					}
					
					if(js==docCount)
					{

						Document doc(docFile->getId() );
						doc.insertPosition(word_offset.offset);
						data->insertDocument(doc);
						indice.update(*data);
					}
					else{
						Document *doc = data->getDocument(js).second;
						if(doc->insertPosition(word_offset.offset)){
							indice.update(*data);
						}
					}*/
					/*
					KeyValId wid(word_offset.word.size(),word_offset.word.c_str(), data->getWordId());
					bsharpTree.insert(wid);*/
					
					regAux.setId( data->getWordId() ); // obtengo el Id del termino a cargar en el archivo auxiliar
					regAux.setOffset( word_offset.offset ); // obtengo el offset del termino en el documento a cargar en el archivo auxiliar
					auxFile->addRegister( regAux.getData() );
					
				}
				else{
					//regDic._Id = data->getWordId();
					//dicFile->addRegister( regDic );
					regAux.setId( data->getWordId() ); // obtengo el Id del termino a cargar en el archivo auxiliar
					regAux.setOffset( word_offset.offset ); // obtengo el offset del termino en el documento a cargar en el archivo auxiliar
					auxFile->addRegister( regAux.getData() );
				}
				

			}
			/*regAux.setId( data->getWordId() ); // obtengo el Id del termino a cargar en el archivo auxiliar
			regAux.setOffset( word_offset.offset ); // obtengo el offset del termino en el documento a cargar en el archivo auxiliar
			auxFile->addRegister( regAux.getData() );*/
			delete data;
			if(progress != NULL)
			{
				auxprog2++;
				int tmp = ((auxprog + 1) *30)/cantidadDocumentos;
				int tmpant = (auxprog *30)/cantidadDocumentos;
				tmp2 = tmpant + (((double)(auxprog2))*(tmp - tmpant))/((double)(cantPalabras));
				if(tmp2 < tmp)
				{
					progress->setValue(tmp2);
				}
			}
		}
		docFile->closeFile();	
		delete docFile;
		docFile = getNextDocument(); // obtengo el siguiente documento txt
		if(progress != NULL)
		{
			int tmp = ((auxprog + 1) *30)/cantidadDocumentos;
			progress->setValue(tmp);
			auxprog++;
		}
	} //fin de while hay documentos
	//IMPORTANTE!!! --->> DEBEMOS GUARDAR EN ALGUN LUGAR EL ULTIMO ID USADO, PARA Q CUANDO REEJECUTE
	//LO TOME
	//cierro todos los archivos
	ptrIDFile->writeLastID(ID);
	ptrIDFile->closeFile();
	delete ptrIDFile;
	//dicFile->closeFile();
	auxFile->closeFile();	
	delete auxFile;	
	auxFile = NULL;

	
	/********** SORT & MERGE ***************/
	string origen("auxiliar.txt");
	string destino("final.txt");
	ExternSort sorter(origen, destino);
	
	if ( !sorter.DivideSort() )  //si hubo error en alguno de los dos no sigo
		return false;
		
	if (!sorter.Merge())
		return false;
		
	//bstree::BSIterator* bsiter = bsharpTree.getFirst();
		
	InvertedIndexData* dataInv;
	
	dataInv = getNextIndexData(&indice,bsharpTree);

	int cantIter = auxFile->size() / auxFile->sizeData();
	while( dataInv != NULL )
	{
		indice.update(*dataInv);
		//dataInv->toOstream(std::cout);
		delete dataInv;
		data=NULL;
		
		dataInv = getNextIndexData(&indice,bsharpTree);
		if(progress != NULL)
		{
			int tmp = 30 + (((contadorReg + 1) *70)/cantIter);
			progress->setValue(tmp);
		}
	}
	//indice.THtoOstream(std::cout);
	//indice.SHtoOstream(std::cout);
	//dicFile->closeFile();
	//dicFile->removeFile();
	//delete dicFile;
	
	auxFile->closeFile();
	auxFile->removeFile();
	delete auxFile;
	
	
	return true;
}
/********************************************************************************************/
InvertedIndexData* DocumentsProcessor::getNextIndexData(InvertedIndex *ii,bstree::BSTree& bstreeID)
{
	tRegDictionary regDic; // contiene termino i Id del termino
	tRegAuxiliary regAux; // contiene el Id de termino, Nº doc, y offset en el mismo
	Document* doc;
	
	if (auxFile== NULL)
	{
		auxFile = new AuxiliaryFile("final.txt");
		if ( !auxFile->openFile() /*|| !dicFile->openFile()*/ )
		{
			delete auxFile;
			//return NULL;
		}
			
	}
	
	if ( auxFile->eof() )
	{
		return NULL;
	}

	/*if(bsiter->EndOfFile()){
		if(!dicFile->eof())
			dicFile->readRegister( regDic );
		else
			return NULL;
	}
	else{
		KeyValId* kid = dynamic_cast<KeyValId*>(bsiter->getRegister());
		regDic._Id=kid->getIdKey();
		std::pair<unsigned int, char*> aux = kid->getKey();
		memcpy(regDic._word, aux.second, aux.first);
		regDic._word[aux.first]='\0';
		delete aux.second;
		delete kid;
		bsiter->sig();
	}*/
	if(!auxFile->eof())
		auxFile->readRegister( regAux );
	else
		return NULL;
	//auxFile->readRegister(regAux);
	
	KeyValId kid(0,NULL,regAux._Id);
	if(!bstreeID.search(kid)){
		std::cout<<"Error en busqueda de palabra por id\n";
		exit(0);
	}
	regDic._Id=kid.getIdKey();
	std::pair<unsigned int, char*> aux = kid.getKey();
	memcpy(regDic._word, aux.second, aux.first);
	regDic._word[aux.first]='\0';
	delete aux.second;

	InvertedIndexData* iData = new InvertedIndexData();
	
	iData->setWordParameters( strlen(regDic._word), regDic._word, regDic._Id );	
	ii->search(*iData);
	tDocumentNumber prev_doc;
	
	

	doc = new Document(regAux._document);
	prev_doc = regAux._document;
	
	while( (regAux._Id == regDic._Id) && (!auxFile->eof()) )
	{
		if (regAux._document == prev_doc )
		{
			doc->insertPosition(regAux._offset);
		}
		else
		{
			iData->insertDocument(*doc);
			//iData->addDocumentNumber( doc );
			prev_doc = regAux._document;
			delete doc;
			doc = new Document(regAux._document);
			//doc->setNumDoc(regAux._document);
			doc->insertPosition(regAux._offset);			
		}
		auxFile->readRegister(regAux);
		contadorReg++;
	}
	//iData->addDocumentNumber( doc );
	iData->insertDocument(*doc);
	//iData->toOstream(std::cout);
	auxFile->back();
	return iData;
}
/********************************************************************************************/
void DocumentsProcessor::setSourceWorkDirectory( string directory )
{
	directory.clear();
}
/********************************************************************************************/
void DocumentsProcessor::setDestinationWorkDirectory( string directory )
{
	directory.clear();
}
/********************************************************************************************/
